#include <core/common/global.h>
#include <core/common/string.h>

//
// class common::String
//

int64 core::common::String::atoi64( const char *value )
{
#ifdef OS_PLATFORM_WINDOWS
	return _atoi64(value);
#else
	return atoll(value);
#endif
}

bool core::common::String::isSpace( const char value )
{
	//
	// isspace will cause assert failed if value < 0
	//
	return (value > 0) && isspace(value);
}

bool core::common::String::isDigit( const char value )
{
	//
	// isdigit will cause assert failed if value < 0
	//
	return (value > 0) && isdigit(value);
}

bool core::common::String::isAlphaNumber( const char value )
{
	//
	// isalnum will cause assert failed if value < 0
	//
	return (value > 0) && isalnum(value);
}

time_t core::common::String::toTimestamp( const std::string &timeValue, bool gmt )
{
	std::string newTime = timeValue;
	core::common::String::replace(newTime, ":", "-");
	core::common::String::replace(newTime, " ", "-");
	core::common::String::replace(newTime, "/", "-");
	core::common::String::replace(newTime, ".", "-");

	core::common::StringArray values;
	if( core::common::String::split(newTime, values, '-') != 6 )
	{
		return 0;
	}

	struct tm nTimeValue = {0};

	nTimeValue.tm_year = atoi(values[0].c_str()) - 1900;
	nTimeValue.tm_mon  = atoi(values[1].c_str()) - 1;
	nTimeValue.tm_mday = atoi(values[2].c_str());
	nTimeValue.tm_hour = atoi(values[3].c_str());
	nTimeValue.tm_min  = atoi(values[4].c_str());
	nTimeValue.tm_sec  = atoi(values[5].c_str());

	time_t localTimestamp = mktime(&nTimeValue);

	if( gmt ) return localTimestamp - core::common::getLocalTimezoneSeconds();
	else return localTimestamp;
}

std::string core::common::String::formatTime( time_t tmValue )
{
	return formatStandardTime(tmValue, false);
}

std::string core::common::String::formatTime( int64 microValue, bool milliSeconds )
{
	char buffer[128] = {0};
	struct tm timeInfo = {0};
	time_t tmValue = (time_t)(microValue / core::common::kMicroUnitsPerSec);
	int    microPart = (int)(microValue % core::common::kMicroUnitsPerSec);

	core::common::getLocalTimestamp(tmValue, timeInfo);

	if( milliSeconds )
	{
		sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
			timeInfo.tm_year + 1900,
			timeInfo.tm_mon + 1,
			timeInfo.tm_mday,
			timeInfo.tm_hour,
			timeInfo.tm_min,
			timeInfo.tm_sec,
			microPart / 1000);
	}
	else
	{
		sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%06d",
			timeInfo.tm_year + 1900,
			timeInfo.tm_mon + 1,
			timeInfo.tm_mday,
			timeInfo.tm_hour,
			timeInfo.tm_min,
			timeInfo.tm_sec,
			microPart);
	}

	return std::string(buffer);
}

std::string core::common::String::formatTime( const std::string &fmt, time_t tmValue, bool includeMark )
{
	struct tm timeInfo = {0};

	core::common::getLocalTimestamp(tmValue, timeInfo);

	std::string timeValue = fmt;
	if( includeMark )
	{
		String::replace(timeValue, "{Y}", String::format("%04d", timeInfo.tm_year + 1900));
		String::replace(timeValue, "{m}", String::format("%02d", timeInfo.tm_mon + 1));
		String::replace(timeValue, "{d}", String::format("%02d", timeInfo.tm_mday));
		String::replace(timeValue, "{H}", String::format("%02d", timeInfo.tm_hour));
		String::replace(timeValue, "{i}", String::format("%02d", timeInfo.tm_min));
		String::replace(timeValue, "{s}", String::format("%02d", timeInfo.tm_sec));
	}
	else
	{
		String::replace(timeValue, "Y", String::format("%04d", timeInfo.tm_year + 1900));
		String::replace(timeValue, "m", String::format("%02d", timeInfo.tm_mon + 1));
		String::replace(timeValue, "d", String::format("%02d", timeInfo.tm_mday));
		String::replace(timeValue, "H", String::format("%02d", timeInfo.tm_hour));
		String::replace(timeValue, "i", String::format("%02d", timeInfo.tm_min));
		String::replace(timeValue, "s", String::format("%02d", timeInfo.tm_sec));
	}

	return timeValue;
}

std::string core::common::String::formatMicroTime( const std::string &fmt, int64 microValue, bool includeMark )
{
	struct tm timeInfo = {0};
	time_t tmValue = (time_t)(microValue / core::common::kMicroUnitsPerSec);
	int    microPart = (int)(microValue % core::common::kMicroUnitsPerSec);

	core::common::getLocalTimestamp(tmValue, timeInfo);

	std::string timeValue = fmt;
	if( includeMark )
	{
		String::replace(timeValue, "{Y}", String::format("%04d", timeInfo.tm_year + 1900));
		String::replace(timeValue, "{m}", String::format("%02d", timeInfo.tm_mon + 1));
		String::replace(timeValue, "{d}", String::format("%02d", timeInfo.tm_mday));
		String::replace(timeValue, "{H}", String::format("%02d", timeInfo.tm_hour));
		String::replace(timeValue, "{i}", String::format("%02d", timeInfo.tm_min));
		String::replace(timeValue, "{s}", String::format("%02d", timeInfo.tm_sec));
		String::replace(timeValue, "{u}", String::format("%03d", microPart / 1000));
		String::replace(timeValue, "{U}", String::format("%06d", microPart));
	}
	else
	{
		String::replace(timeValue, "Y", String::format("%04d", timeInfo.tm_year + 1900));
		String::replace(timeValue, "m", String::format("%02d", timeInfo.tm_mon + 1));
		String::replace(timeValue, "d", String::format("%02d", timeInfo.tm_mday));
		String::replace(timeValue, "H", String::format("%02d", timeInfo.tm_hour));
		String::replace(timeValue, "i", String::format("%02d", timeInfo.tm_min));
		String::replace(timeValue, "s", String::format("%02d", timeInfo.tm_sec));
		String::replace(timeValue, "u", String::format("%03d", microPart / 1000));
		String::replace(timeValue, "U", String::format("%06d", microPart));
	}

	return timeValue;
}

std::string core::common::String::formatStandardTime( time_t tmValue, bool gmt )
{
	char buffer[128] = {0};
	struct tm timeInfo = {0};

	if( gmt ) core::common::getGmtTimestamp(tmValue, timeInfo);
	else core::common::getLocalTimestamp(tmValue, timeInfo);

	sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d",
		timeInfo.tm_year + 1900,
		timeInfo.tm_mon + 1,
		timeInfo.tm_mday,
		timeInfo.tm_hour,
		timeInfo.tm_min,
		timeInfo.tm_sec);

	return std::string(buffer);
}

std::string core::common::String::formatLongTime( time_t tmValue, bool gmt )
{
	static const char *weekNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
	static const char *monthNames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
	struct tm timeInfo = {0};

	if( gmt ) core::common::getGmtTimestamp(tmValue, timeInfo);
	else core::common::getLocalTimestamp(tmValue, timeInfo);

	if( timeInfo.tm_wday < 0 || timeInfo.tm_wday >= 7 )
	{
		timeInfo.tm_wday = 0;
	}

	if( timeInfo.tm_mon < 0 || timeInfo.tm_mon >= 12 )
	{
		timeInfo.tm_mon = 0;
	}

	return core::common::String::format("%s, %02d %s %04d %02d:%02d:%02d%s", 
		weekNames[timeInfo.tm_wday],
		timeInfo.tm_mday, 
		monthNames[timeInfo.tm_mon],
		timeInfo.tm_year + 1900,
		timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec,
		gmt ? " GMT" : "");
}

std::string core::common::String::formatRelativeTime( int64 microValue )
{
	char buffer[128] = {0};
	struct tm timeInfo = {0};
	time_t tmValue = (time_t)(microValue / core::common::kMicroUnitsPerSec);
	int    microPart = (int)(microValue % core::common::kMicroUnitsPerSec);

	core::common::getGmtTimestamp(tmValue, timeInfo);

	sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
		timeInfo.tm_year - 70,
		timeInfo.tm_mon,
		timeInfo.tm_mday - 1,
		timeInfo.tm_hour,
		timeInfo.tm_min,
		timeInfo.tm_sec,
		microPart / 1000);

	return std::string(buffer);
}

std::string core::common::String::getLocalTime( bool milliSeconds )
{
#ifdef OS_PLATFORM_WINDOWS
	SYSTEMTIME systemTime;
	::GetLocalTime(&systemTime);

	char buffer[128] = {0};

	if( milliSeconds )
	{
		sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
			systemTime.wYear,
			systemTime.wMonth,
			systemTime.wDay,
			systemTime.wHour,
			systemTime.wMinute,
			systemTime.wSecond,
			systemTime.wMilliseconds);
	}
	else
	{
		sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%06d",
			systemTime.wYear,
			systemTime.wMonth,
			systemTime.wDay,
			systemTime.wHour,
			systemTime.wMinute,
			systemTime.wSecond,
			(int(systemTime.wMilliseconds)) * 1000);
	}

	return std::string(buffer);
#else
	return formatTime(core::common::getMicroTime(), true);
#endif
}

std::string core::common::String::formatIpv4( uint32 nIpv4 )
{
	char buffer[64];
	sprintf(buffer, "%u.%u.%u.%u", (nIpv4 >> 24) & 0xff, (nIpv4 >> 16) & 0xff, (nIpv4 >> 8) & 0xff, nIpv4 & 0xff);

	return std::string(buffer);
}

std::string core::common::String::formatMacAddress( uint8 *macAddress )
{
	char buffer[64];
	sprintf(buffer, "%02X:%02X:%02X:%02X:%02X:%02X", 
		macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]);

	return std::string(buffer);
}

std::string core::common::String::formatFileSize( uint64 size )
{
	std::string formatSize;
	if( size < core::common::kByteUnitsPerKilo )
	{
		formatSize = core::common::String::format(_I64FMT_" Bytes", size);
	}
	else if( size < core::common::kByteUnitsPerMega )
	{
		formatSize = core::common::String::format("%.1f KB", (double)size / core::common::kByteUnitsPerKilo);
	}
	else if( size < core::common::kByteUnitsPerGiga )
	{
		formatSize = core::common::String::format("%.1f MB", (double)size / core::common::kByteUnitsPerMega);
	}
	else if( size < core::common::kByteUnitsPerTera )
	{
		formatSize = core::common::String::format("%.1f GB", (double)size / core::common::kByteUnitsPerGiga);
	}
	else
	{
		formatSize = core::common::String::format("%.1f TB", (double)size / core::common::kByteUnitsPerTera);
	}
	return formatSize;
}

std::string  core::common::String::formatSpeed( uint64 speed, bool unitBits, bool shortName )
{
	std::string result;
	if( !unitBits )
	{
		if( speed < core::common::kByteUnitsPerKilo )
		{
			result = core::common::String::format(_I64FMT_"%s", speed, (shortName ? " B" : " B/s"));
		}
		else if( speed < core::common::kByteUnitsPerMega )
		{
			result = core::common::String::format("%.1f%s", (double)speed / core::common::kByteUnitsPerKilo, (shortName ? " K" : " KB/s"));
		}
		else if( speed < core::common::kByteUnitsPerGiga )
		{
			result = core::common::String::format("%.1f%s", (double)speed / core::common::kByteUnitsPerMega, (shortName ? " M" : " MB/s"));
		}
		else
		{
			result = core::common::String::format("%.1f%s", (double)speed / core::common::kByteUnitsPerGiga, (shortName ? " G" : " GB/s"));
		}
	}
	else
	{
		if( speed < 1000 )
		{
			result = core::common::String::format(_I64FMT_"%s", speed, (shortName ? " b" : " bps"));
		}
		else if( speed < 1000 * 1000 )
		{
			result = core::common::String::format("%.1f%s", (double)speed / 1000, (shortName ? " K" : " Kbps"));
		}
		else if( speed < 1000 * 1000 * 1000 )
		{
			result = core::common::String::format("%.1f%s", (double)speed / (1000 * 1000), (shortName ? " M" : " Mbps"));
		}
		else
		{
			result = core::common::String::format("%.1f%s", (double)speed / (1000 * 1000 * 1000), (shortName ? " G" : " Gbps"));
		}
	}

	return result;
}

std::string core::common::String::formatTimeLength( time_t timeLength )
{
	if( timeLength > core::common::kSecondUnitsPerDay )
	{
		return core::common::String::format("%d %d:%d:%d", 
			(int)(timeLength / core::common::kSecondUnitsPerDay),
			(int)((timeLength % core::common::kSecondUnitsPerDay) / core::common::kSecondUnitsPerHour),
			(int)((timeLength % core::common::kSecondUnitsPerHour) / core::common::kSecondUnitsPerMinute),
			(int)(timeLength % core::common::kSecondUnitsPerMinute));
	}
	else if( timeLength > core::common::kSecondUnitsPerHour )
	{
		return core::common::String::format("%d:%d:%d", 
			(int)((timeLength % core::common::kSecondUnitsPerDay) / core::common::kSecondUnitsPerHour),
			(int)((timeLength % core::common::kSecondUnitsPerHour) / core::common::kSecondUnitsPerMinute),
			(int)(timeLength % core::common::kSecondUnitsPerMinute));
	}
	else if( timeLength > core::common::kSecondUnitsPerMinute )
	{
		return core::common::String::format("%d:%d", 
			(int)((timeLength % core::common::kSecondUnitsPerHour) / core::common::kSecondUnitsPerMinute),
			(int)(timeLength % core::common::kSecondUnitsPerMinute));
	}
	else
	{
		return core::common::String::format("%d", (int)timeLength);
	}
}

std::string core::common::String::format( const char *fmt, ... )
{
	va_list argList;

	va_start(argList, fmt);
	std::string strRes = String::formatV(fmt, argList);
	va_end(argList);

	return strRes;
}

std::string core::common::String::formatV( const char *fmt, va_list argList )
{
	std::string result;
	int   size = 0;
	char *buffer = NULL;

#ifdef OS_PLATFORM_WINDOWS
	size = _vscprintf(fmt, argList);
#else
	va_list argCopy;
	va_copy(argCopy, argList);
	size = vsnprintf(NULL, 0, fmt, argCopy);
	va_end(argCopy);
#endif //OS_PLATFORM_WINDOWS

	buffer = new char[size + 1];
	buffer[size] = 0;
	vsprintf(buffer, fmt, argList);

	result = buffer;
	delete []buffer;

	return result;
}

std::string &core::common::String::appendFormat( std::string &org, const char *fmt, ... )
{
	va_list argList;

	va_start(argList, fmt);
	std::string append = String::formatV(fmt, argList);
	va_end(argList);

	org.append(append);
	return org;
}

std::string core::common::String::toUpper( const std::string &source )
{
	std::string result = source;
	std::transform(result.begin(), result.end(), result.begin(), toupper);
	return result;
}

std::string core::common::String::toLower( const std::string &source )
{
	std::string result = source;
	std::transform(result.begin(), result.end(), result.begin(), tolower);
	return result;
}

std::string &core::common::String::makeUpper( std::string &source )
{
	std::transform(source.begin(), source.end(), source.begin(), toupper);
	return source;
}

std::string &core::common::String::makeLower( std::string &source )
{
	std::transform(source.begin(), source.end(), source.begin(), tolower);
	return source;
}

std::string &core::common::String::trim( std::string &source )
{
	if( source.size() == 0 ) return source;

	size_t leftPos = 0, rightPos = source.size() - 1;
	while( leftPos < source.size() && isSpace(source[leftPos]) ) leftPos ++;
	while( rightPos >= leftPos && isSpace(source[rightPos]) )
	{
		if( rightPos == 0 ) break; 
		rightPos --;
	}

	if( leftPos > rightPos ) 
	{
		source.clear();
	}
	else 
	{
		std::string temp = source.substr(leftPos, rightPos - leftPos + 1);
		source = temp;
	}

	return source;
}

std::string &core::common::String::trimLeft( std::string &source )
{
	if( source.size() == 0 ) return source;

	size_t leftPos = 0, rightPos = source.size() - 1;
	while( leftPos < source.size() && isSpace(source[leftPos]) ) leftPos ++;

	if( leftPos > rightPos ) 
	{
		source.clear();
	}
	else 
	{
		std::string strTrim = source.substr(leftPos, rightPos - leftPos + 1);
		source = strTrim;
	}

	return source;
}

std::string &core::common::String::trimRight( std::string &source )
{
	if( source.size() == 0 ) return source;

	size_t leftPos = 0, rightPos = source.size() - 1;
	while( rightPos >= leftPos && isSpace(source[rightPos]) )
	{
		if( rightPos == 0 ) break; 
		rightPos --;
	}

	if( leftPos > rightPos ) 
	{
		source.clear();
	}
	else 
	{
		std::string temp = source.substr(leftPos, rightPos - leftPos + 1);
		source = temp;
	}

	return source;
}

int core::common::String::compareVersion( const std::string &version1, const std::string &version2, const std::string &separator )
{
	core::common::StringArray version1Array;
	core::common::StringArray version2Array;
	split(version1, version1Array, separator);
	split(version2, version2Array, separator);

	// padding for 0
	size_t versionLength = core::common::maxValue(version1Array.size(), version2Array.size());
	for( size_t i = 0; i < versionLength; i ++ )
	{
		if( version1Array.size() <= i ) version1Array.push_back("0");
		if( version2Array.size() <= i ) version2Array.push_back("0");
	}

	for( size_t i = 0; i < versionLength; i ++ )
	{
		int32 version1ItemValue = 0;
		int32 version2ItemValue = 0;
		version1ItemValue = core::common::String::parseNumber(version1Array[i], version1ItemValue);
		version2ItemValue = core::common::String::parseNumber(version2Array[i], version2ItemValue);
		if( version1ItemValue != version2ItemValue )
		{
			return version1ItemValue - version2ItemValue;
		}
	}

	return 0;
}

int core::common::String::replace( std::string &org, const std::string &old, const std::string &newString )
{
	int count = 0;
	std::string::size_type pos = 0;

	if( old.size() == 0 ) return 0;
	while( (pos = org.find(old, pos)) != std::string::npos )
	{
		org.replace(pos, old.size(), newString);
		pos += newString.size();
		count ++;
	}

	return count;
}

std::string core::common::String::replaceTo( const std::string &org, const std::string &old, const std::string &newString )
{
	std::string replaced = org;
	String::replace(replaced, old, newString);
	return replaced;
}

int core::common::String::replaceNonAscii( std::string &org, const char newChar )
{
	int count = 0;

	for( size_t n = 0; n < org.size(); n ++ )
	{
		const char charValue = org[n];
		if( charValue <= 0 || charValue >= 127 )
		{
			org[n] = newChar;
			count ++;
		}
	}

	return count;
}

std::string core::common::String::replaceNonAsciiTo( const std::string &org, const char newChar )
{
	std::string newValue = org;
	replaceNonAscii(newValue, newChar);
	return newValue;
}

int core::common::String::split( const std::string &content, 
						 core::common::StringList &values, 
						 const char key, 
						 int maxItems, 
						 bool ignoreEmpty,
						 size_t contentLength )
{

	return String::split(content, values, std::string((const char *)&key, 1), maxItems, ignoreEmpty, contentLength);
}

int core::common::String::split( const std::string &content, 
						 core::common::StringList &values, 
						 const std::string &key, 
						 int maxItems, 
						 bool ignoreEmpty,
						 size_t contentLength )
{
	int startPos = 0;
	int findPos = 0;
	int itemCount = 0;
	int keyLen = (int)key.size();
	int endPos = (std::string::npos == contentLength) ? (int)content.size() : (int)contentLength;

	values.clear();

	if( key.size() == 0 ) return 0;
	else if( endPos <= 0 || endPos >= (int)content.size() )
	{
		endPos = (int)content.size();
	}

	while( startPos < (int)endPos )
	{
		findPos = (int)content.find(key.c_str(), startPos);
		if( findPos < 0 || findPos >= endPos || (maxItems > 0 && itemCount == maxItems - 1) )
		{
			findPos = endPos;
		}

		if( findPos < startPos )
		{
			break;
		}

		if( findPos > startPos || !ignoreEmpty )
		{
			std::string strNewValue = (findPos > startPos) ? content.substr(startPos, findPos - startPos) : std::string("");
			values.push_back(strNewValue);
			itemCount ++;
		}

		startPos = findPos + keyLen;
	}

	return itemCount;
}

int core::common::String::split( const std::string &content, 
						 core::common::StringArray &values, 
						 const char key, 
						 int maxItems, 
						 bool ignoreEmpty,
						 size_t contentLength )
{
	return String::split(content, values, std::string((const char *)&key, 1), maxItems, ignoreEmpty, contentLength);
}

int core::common::String::split( const std::string &content, 
						 core::common::StringArray &values, 
						 const std::string &key, 
						 int maxItems, 
						 bool ignoreEmpty,
						 size_t contentLength )
{
	int startPos = 0;
	int findPos = 0;
	int itemCount = 0;
	int keyLen = (int)key.size();
	int endPos = (std::string::npos == contentLength) ? (int)content.size() : (int)contentLength;

	values.clear();
	
	if( key.size() == 0 ) return 0;
	if( endPos <= 0 || endPos >= (int)content.size() )
	{
		endPos = (int)content.size();
	}

	while( startPos < endPos )
	{
		findPos = (int)content.find(key.c_str(), startPos);
		if( findPos < 0 || findPos >= endPos || (maxItems > 0 && itemCount == maxItems - 1) )
		{
			findPos = endPos;
		}

		if( findPos < startPos )
		{
			break;
		}

		if( findPos > startPos || !ignoreEmpty )
		{
			std::string strNewValue = (findPos > startPos) ? content.substr(startPos, findPos - startPos) : std::string("");
			values.push_back(strNewValue);
			itemCount ++;
		}

		startPos = findPos + keyLen;
	}

	return itemCount;
}

int core::common::String::parseAttributes( const std::string &content,
								   StringMap &attributes, 
								   const char separatorKey, 
								   const char valueKey,
								   bool keyLowCase,
								   bool trimKey,
								   bool trimValue )
{
	int lines = 0;
	StringList parts;

	split(content, parts, separatorKey);

	for( StringList::iterator itr = parts.begin(); itr != parts.end(); itr ++ )
	{
		std::string &partValue = (*itr);
		StringList partValues;
		if( split(partValue, partValues, valueKey, 2) == 0 )
		{
			continue;
		}

		if( keyLowCase )
		{
			core::common::String::makeLower(partValues.front());
		}

		if( trimKey )
		{
			core::common::String::trim(partValues.front());
		}

		if( trimValue && partValues.size() > 1 )
		{
			core::common::String::trim(partValues.back());
		}

		if( partValues.size() < 2 )
		{
			attributes[partValues.front()] = "";
		}
		else
		{
			attributes[partValues.front()] = partValues.back();
		}

		lines ++;
	}

	return lines;
}

int core::common::String::compare( const std::string &str1, const std::string &str2, bool ignoreCase /* = false */ )
{
	if( !ignoreCase )
	{
		return strcmp(str1.c_str(), str2.c_str());
	}
	else
	{
#ifdef OS_PLATFORM_WINDOWS
		return _stricmp(str1.c_str(), str2.c_str());
#else
		return strcasecmp(str1.c_str(), str2.c_str());
#endif
	}
}

int core::common::String::compareTo( const char *str1, const char *str2, bool ignoreCase /* = false */ )
{
	if( !ignoreCase )
	{
		return strcmp(str1, str2);
	}
	else
	{
#ifdef OS_PLATFORM_WINDOWS
		return _stricmp(str1, str2);
#else
		return strcasecmp(str1, str2);
#endif
	}
}

//
// string <=> integer
//

uint32 core::common::String::parseNumber( const std::string &value, const uint32 defaultValue )
{
	if( value.size() == 0 )
	{
		return defaultValue;
	}

	const char &tag = value[0];
	uint32 result = 0;
	size_t usedDigits = 0;

	if( tag >= '0' && tag <= '9' )
	{
		for( int i = 0; i < (int)value.size(); i ++ )
		{
			char itemValue = value[i];
			if( itemValue >= '0' && itemValue <= '9')
			{
				result *= 10;
				result += (uint32)(itemValue - '0');
				usedDigits ++;
			}
			else if( !isSpace(itemValue) || usedDigits > 0 )
			{
				break;
			}
		}
	}
	else
	{
		switch( tag )
		{
		case 'X':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '9')
				{
					result <<= 4;
					result += (uint32)(itemValue - '0');
				}
				else if( itemValue >= 'A' && itemValue <= 'F' )
				{
					result <<= 4;
					result += (uint32)(itemValue - 'A');
					result += 10;
				}
				else
				{
					break;
				}
			}
			break;
		case 'B':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '1')
				{
					result <<= 1;
					result += (uint32)(itemValue - '0');
				}
				else
				{
					break;
				}
			}
			break;
		default:
			result = defaultValue;
			break;
		}
	}

	return result;
}

int32 core::common::String::parseNumber( const std::string &value, const int32 defaultValue )
{
	if( value.size() == 0 )
	{
		return defaultValue;
	}

	const char &tag = value[0];
	int32 result = 0;

	if( tag == '+' || tag == '-' || isdigit(tag) || isSpace(tag) )
	{
		result = (int32)atoi(value.c_str());
	}
	else
	{
		switch( tag )
		{
		case 'X':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '9')
				{
					result <<= 4;
					result += (int32)(itemValue - '0');
				}
				else if( itemValue >= 'A' && itemValue <= 'F' )
				{
					result <<= 4;
					result += (int32)(itemValue - 'A');
					result += 10;
				}
				else
				{
					break;
				}
			}
			break;
		case 'B':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '1')
				{
					result <<= 1;
					result += (int32)(itemValue - '0');
				}
				else
				{
					break;
				}
			}
			break;
		default:
			result = defaultValue;
			break;
		}
	}

	return result;
}

uint64 core::common::String::parseNumber( const std::string &value, const uint64 defaultValue )
{
	if( value.size() == 0 )
	{
		return defaultValue;
	}

	const char &tag = value[0];
	uint64 result = 0;
	size_t usedDigits = 0;

	if( tag >= '0' && tag <= '9' )
	{
		for( int i = 0; i < (int)value.size(); i ++ )
		{
			char itemValue = value[i];
			if( itemValue >= '0' && itemValue <= '9')
			{
				result *= 10;
				result += (uint64)(itemValue - '0');
				usedDigits ++;
			}
			else if( !isSpace(itemValue) || usedDigits > 0 )
			{
				break;
			}
		}
	}
	else
	{
		switch( tag )
		{
		case 'X':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '9')
				{
					result <<= 4;
					result += (uint64)(itemValue - '0');
				}
				else if( itemValue >= 'A' && itemValue <= 'F' )
				{
					result <<= 4;
					result += (uint64)(itemValue - 'A');
					result += 10;
				}
				else
				{
					break;
				}
			}
			break;
		case 'B':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '1')
				{
					result <<= 1;
					result += (uint64)(itemValue - '0');
				}
				else
				{
					break;
				}
			}
			break;
		default:
			result = defaultValue;
			break;
		}
	}

	return result;
}

int64 core::common::String::parseNumber( const std::string &value, const int64 defaultValue )
{
	if( value.size() == 0 )
	{
		return defaultValue;
	}

	const char &tag = value[0];
	int64 result = 0;

	if( tag == '+' || tag == '-' || isdigit(tag) || isSpace(tag) )
	{
#ifdef OS_PLATFORM_WINDOWS
		result = _atoi64(value.c_str());
#else
		result = atoll(value.c_str());
#endif
	}
	else
	{
		switch( tag )
		{
		case 'X':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '9')
				{
					result <<= 4;
					result += (int64)(itemValue - '0');
				}
				else if( itemValue >= 'A' && itemValue <= 'F' )
				{
					result <<= 4;
					result += (int64)(itemValue - 'A');
					result += 10;
				}
				else
				{
					break;
				}
			}
			break;
		case 'B':
			for( int i = 1; i < (int)value.size(); i ++ )
			{
				char itemValue = (char)toupper(value[i]);
				if( itemValue >= '0' && itemValue <= '1')
				{
					result <<= 1;
					result += (int64)(itemValue - '0');
				}
				else
				{
					break;
				}
			}
			break;
		default:
			return defaultValue;
		}
	}

	return result;
}

double core::common::String::parseNumber( const std::string &value, const double defaultValue )
{
	if( value.size() == 0 )
	{
		return defaultValue;
	}

	return atof(value.c_str());
}

uint64 core::common::String::parseNumberX64( const std::string &value, const uint64 defaultValue )
{
	uint64 result = 0;
	size_t usedDigits = 0;

	char firstDigit = value[0];
	if( value.size() == 0 || !((firstDigit >= '0' && firstDigit <= '9') || 
		(firstDigit >= 'A' && firstDigit <= 'F') || 
		(firstDigit >= 'a' && firstDigit <= 'f')) )
	{
		return defaultValue;
	}

	for( int i = 0; i < (int)value.size(); i ++ )
	{
		char itemValue = (char)toupper(value[i]);
		if( itemValue >= '0' && itemValue <= '9')
		{
			result <<= 4;
			result += (uint64)(itemValue - '0');
			usedDigits ++;
		}
		else if( itemValue >= 'A' && itemValue <= 'F' )
		{
			result <<= 4;
			result += (uint64)(itemValue - 'A');
			result += 10;
			usedDigits ++;
		}
		else if( !isSpace(itemValue) || usedDigits > 0 )
		{
			break;
		}
	}

	return result;
}

int64 core::common::String::parseLocalTime( const std::string &timeValue )
{
	int64 microPart = 0;
	size_t pos = timeValue.find(".");
	std::string newTime = timeValue;
	if( pos != std::string::npos && (pos + 1) < newTime.size() )
	{
		std::string microPartString = newTime.substr(pos + 1);
		while( microPartString.size() > 1 && (microPartString[0] == '0' || core::common::String::isSpace(microPartString[0])) )
		{
			microPartString.erase(0, 1);
		}
		microPart = core::common::String::parseNumber(microPartString, (int64)0);
		newTime.resize(pos);
	}

	pos = newTime.find(' ');
	if( pos == std::string::npos || 0 == pos || (pos + 1) >= newTime.size() )
	{
		return 0;
	}

	core::common::StringArray dateParts;
	core::common::StringArray timeParts;
	core::common::String::split(newTime.substr(0, pos), dateParts, '-');
	core::common::String::split(newTime.substr(pos + 1), timeParts, ':');
	if( dateParts.size() < 3 || timeParts.size() < 3 )
	{
		return 0;
	}

	for( size_t n = 0; n < dateParts.size(); n ++ )
	{
		std::string &value = dateParts[n];
		while( value.size() > 1 && (value[0] == '0' || core::common::String::isSpace(value[0])) )
		{
			value.erase(0, 1);
		}
	}

	for( size_t n = 0; n < timeParts.size(); n ++ )
	{
		std::string &value = timeParts[n];
		while( value.size() > 1 && (value[0] == '0' || core::common::String::isSpace(value[0])) )
		{
			value.erase(0, 1);
		}
	}

	struct tm nTimeValue = {0};
	nTimeValue.tm_year = core::common::String::parseNumber(dateParts[0], (int32)0) - 1900;
	nTimeValue.tm_mon  = core::common::String::parseNumber(dateParts[1], (int32)0) - 1;
	nTimeValue.tm_mday = core::common::String::parseNumber(dateParts[2], (int32)0);
	nTimeValue.tm_hour = core::common::String::parseNumber(timeParts[0], (int32)0);
	nTimeValue.tm_min  = core::common::String::parseNumber(timeParts[1], (int32)0);
	nTimeValue.tm_sec  = core::common::String::parseNumber(timeParts[2], (int32)0);
	time_t nTimestamp = mktime(&nTimeValue);

	return int64(nTimestamp) * core::common::kMicroUnitsPerSec + microPart;
}

std::string core::common::String::fromNumber( const uint64 number )
{
	return core::common::String::format(_U64FMT_, number);
}

std::string core::common::String::fromNumber( const int64 number )
{
	return core::common::String::format(_I64FMT_, number);
}

std::string core::common::String::fromNumber( const uint32 number )
{
	return core::common::String::format("%u", number);
}

std::string core::common::String::fromNumber( const int32 number )
{
	return core::common::String::format("%d", number);
}

std::string core::common::String::fromNumber( const double number )
{
	return core::common::String::format("%.10g", number);
}

size_t core::common::String::base64EncodeSize( size_t length )
{
	return ((length + 2) / 3 * 4);
}

size_t core::common::String::base64DecodeSize( size_t length )
{
	return ((length + 3) / 4 * 3);
}

bool core::common::String::base64Encode( const uint8 *source, size_t sourceLength, char *dest, size_t &destLength )
{
	const char *encodeTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	size_t n = 0, p = 0;

	// insufficient output buffer
	if( destLength < base64EncodeSize(sourceLength) ) return false;

	while( n + 2 < sourceLength )
	{
		uint8 b0 = (source[n] >> 2) & 0x3F;
		uint8 b1 = ((source[n] << 4) | (source[n + 1] >> 4)) & 0x3F;
		uint8 b2 = ((source[n + 1] << 2) | (source[n + 2] >> 6)) & 0x3F;
		uint8 b3 = source[n + 2] & 0x3F;

		n += 3;

		dest[p ++] = encodeTable[b0];
		dest[p ++] = encodeTable[b1];
		dest[p ++] = encodeTable[b2];
		dest[p ++] = encodeTable[b3];
	}

	if( n < sourceLength )
	{
		char pad[3];

		for( size_t x = 0; x < 3; x ++ )
		{
			pad[x] = (n + x < sourceLength) ? source[n + x] : 0;
		}

		uint8 b0 = (pad[0] >> 2) & 0x3F;
		uint8 b1 = ((pad[0] << 4) | (pad[1] >> 4)) & 0x3F;
		uint8 b2 = ((pad[1] << 2) | (pad[2] >> 6)) & 0x3F;

		dest[p ++] = encodeTable[b0];
		dest[p ++] = encodeTable[b1];
		dest[p ++] = (sourceLength > n + 1) ? encodeTable[b2] : '=';
		dest[p ++] = '=';
	}

	// add terminate character if buffer is enough
	if( destLength > p ) dest[p] = 0;
	destLength = p;

	return true;
}

bool core::common::String::base64Decode( const char *source, size_t sourceLength, uint8 *dest, size_t &destLength )
{
	size_t n = 0, p = 0;
	static const uint8 decodeTable[256] = 
	{
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 62,  255, 255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
		255, 0,   255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
		15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  255, 255, 255, 255, 255, 255, 26,  27,  28,
		29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
		49,  50,  51,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
	};

	if( 0 == sourceLength ) sourceLength = strlen(source);

	// invalid input size or insufficient output buffer
	if( sourceLength % 4 != 0 ) return false;

	while( n < sourceLength && p < destLength )
	{
		dest[p ++] = (decodeTable[(int)source[n]] << 2) | (decodeTable[(int)source[n + 1]] >> 4);
		if( '=' == source[n + 2] ){ n += 4; break; }

		if( p >= destLength ) return false;
		dest[p ++] = (decodeTable[(int)source[n + 1]] << 4) | (decodeTable[(int)source[n + 2]] >> 2);
		if( '=' == source[n + 3] ){ n += 4; break; }

		if( p >= destLength ) return false;
		dest[p ++] = (decodeTable[(int)source[n + 2]] << 6) | decodeTable[(int)source[n + 3]];

		n += 4;
	}

	if( p >= destLength && n < sourceLength ) return false;

	// add terminate character if buffer is enough
	if( destLength > p ) dest[p] = 0;
	destLength = p;

	return true;
}

std::string core::common::String::base64Encode( const std::string &source )
{
	std::string encoded;
	size_t encodeSize = base64EncodeSize(source.size());
	char *newBuffer = new char[encodeSize + 1];

	do 
	{
		if( !base64Encode((const uint8 *)source.data(), source.size(), newBuffer, encodeSize) )
		{
			break;
		}

		encoded.assign(newBuffer, encodeSize);
	} while (false);

	delete []newBuffer;
	return encoded;
}

std::string core::common::String::base64Decode( const std::string &source )
{
	std::string decoded;
	size_t decodeSize = base64DecodeSize(source.size());
	uint8 *newBuffer = new uint8[decodeSize + 1];

	do 
	{
		if( !base64Decode(source.data(), source.size(), newBuffer, decodeSize) )
		{
			break;
		}

		decoded.assign((const char *)newBuffer, decodeSize);
	} while (false);

	delete []newBuffer;
	return decoded;
}

std::string core::common::String::urlEncode( const std::string &name )
{
	std::string encode;
	for( size_t n = 0; n < name.size(); n ++ )
	{
		const char charValue = name[n];
		if( charValue > 0 && isalnum(charValue) )
		{
			encode.append(1, charValue);
		}
		// encode '+' as %20
		//else if( ' ' == charValue )
		//{
		//	encode.append(1, '+');
		//}
		else
		{
			core::common::String::appendFormat(encode, "%%%02X", (unsigned char)charValue);
		}
	}

	return encode;
}

std::string core::common::String::urlEncodeNonAscii( const std::string &name )
{
	std::string encode;
	for( size_t n = 0; n < name.size(); n ++ )
	{
		const char charValue = name[n];
		if( charValue > 0x20 && charValue < 0x7F )
		{
			encode.append(1, charValue);
		}
		// encode '+' as %20
		//else if( ' ' == charValue )
		//{
		//	encode.append(1, '+');
		//}
		else
		{
			core::common::String::appendFormat(encode, "%%%02X", (unsigned char)charValue);
		}
	}

	return encode;
}

std::string core::common::String::urlDecode( const std::string &name )
{
	std::string decode;

	for( size_t n = 0; n < name.size(); n ++ )
	{
		if( '%' == name[n] )
		{
			if( n + 2 >= name.size() ) break;
			char v1 = name[n + 1];
			char v2 = name[n + 2];
			
			if( v1 >= '0' && v1 <= '9' ) v1 -= '0';
			else if( v1 >= 'a' && v1 <= 'f' ) v1 = v1 - 'a' + 10;
			else if( v1 >= 'A' && v1 <= 'F' ) v1 = v1 - 'A' + 10;

			if( v1 == name[n + 1] ) break;

			if( v2 >= '0' && v2 <= '9' ) v2 -= '0';
			else if( v2 >= 'a' && v2 <= 'f' ) v2 = v2 - 'a' + 10;
			else if( v2 >= 'A' && v2 <= 'F' ) v2 = v2 - 'A' + 10;

			if( v2 == name[n + 2] ) break;

			unsigned char v = (((unsigned char )v1) << 4) | ((unsigned char )v2);
			decode.append(1, (char )v);
			n += 2;
		}
		else if( '+' == name[n] )
		{
			decode.append(1, ' ');
		}
		else
		{
			decode.append(1, name[n]);
		}
	}

	return decode;
}

std::string core::common::String::bytesToHexString( const uint8 *data, size_t length, bool upperCase )
{
	std::string hexString;
	for( size_t n = 0; n < length; n++ )
	{
		core::common::String::appendFormat(hexString, upperCase ? "%02X" : "%02x", data[n]);
	}
	return hexString;
}

std::string core::common::String::bytesToHexString( const std::string &data, bool upperCase )
{
	return bytesToHexString((const uint8 *)data.data(), data.size(), upperCase);
}

std::string core::common::String::hexStringToBytes( const std::string &data )
{
	std::string bytes;
	bytes.resize(data.size() / 2);
	for( size_t n = 0; (n + 1) < data.size(); n = n + 2 )
	{
		std::string temp = std::string("X") + data.substr(n, 2);
		bytes[n / 2] = core::common::String::parseNumber(temp, (int32)0);
	}
	return bytes;
}

uint8 *core::common::String::hexStringToBytes( const std::string &data, uint8 *buffer, size_t length )
{
	for( size_t n = 0; (n + 1) < data.size() && (n / 2) < length; n = n + 2 )
	{
		std::string temp = std::string("X") + data.substr(n, 2);
		buffer[n / 2] = core::common::String::parseNumber(temp, (int32)0);
	}
	return buffer;
}

std::string &core::common::String::jsonEncode( std::string &value )
{
	core::common::String::replace(value, "\\", "\\\\");
	core::common::String::replace(value, "'", "\\'");
	//core::common::String::replace(value, "\"", "\\\"");

	return value;
}

//
// parse url such as:
// http://host:port/path/name
// ftp://host:port/path/name
// xx.xx.xx.xx:port/name
// file include path and name, such as  /path/name
// ...
//
bool core::common::String::parseUrl( const std::string &url, 
			  std::string &protocol,
			  std::string &host,
			  uint16 &port,
			  std::string &path,
			  std::string &file )
{
	protocol = "";
	host = "";
	port = 0;
	path = "";
	file = "";

	size_t protocolPos = url.find(":");
	size_t hostPos = 0;
	if( std::string::npos != protocolPos )
	{
		bool validProtocol = true;
		for( size_t n = 0; n < protocolPos; n ++ )
		{
			if( !isalnum(url[n]) )
			{
				validProtocol = false;
				break;
			}
		}

		if( validProtocol )
		{
			protocol = core::common::String::toLower(url.substr(0, protocolPos));
			hostPos = protocolPos + 1;
			while( hostPos < url.size() && '/' == url[hostPos] )
			{
				hostPos ++;
			}
		}
	}

	size_t portPos = url.find(":", hostPos);
	size_t pathPos = url.find("/", hostPos);
	if( portPos > pathPos )
	{
		// maybe such url http://server/about:blank
		portPos = std::string::npos;
	}

	if( std::string::npos != portPos )
	{
		host = url.substr(hostPos, portPos - hostPos);
		port   = core::common::String::parseNumber(url.substr(portPos + 1), (uint16)0);
	}

	if( std::string::npos == pathPos )
	{
		file = "/";
		if( host.size() == 0 )
		{
			host = url.substr(hostPos);
		}
	}
	else
	{
		file = url.substr(pathPos);
		if( host.size() == 0 )
		{
			host = url.substr(hostPos, pathPos - hostPos);
		}
	}

	size_t filePos = file.rfind('/', file.find('?', 0));
	if( filePos == std::string::npos || filePos == 0 )
	{
		path = "/";
	}
	else
	{
		path = file.substr(0, filePos);
	}

	// format path as /name/
	if( path.size() == 0 ||
		path[path.size() - 1] != '/' )
	{
		path += "/";
	}

	core::common::String::normalizeUrlPath(path);
	core::common::String::normalizeUrlPath(file);

	return true;
}

int core::common::String::normalizeUrlPath( std::string &path )
{
	int count = 0;

	size_t nameEndPos = path.find('?');
	size_t anchorPos = path.find('#');
	size_t protocolPos = path.find("://");

	if( protocolPos > nameEndPos || protocolPos > anchorPos )
	{
		protocolPos = std::string::npos;
	}

	size_t validPos = 0;
	if( protocolPos > 0 )
	{
		validPos = protocolPos + 3;
		while( validPos < path.size() && path[validPos] != '/' )
		{
			validPos ++;
		}
	}

	size_t markPos = path.find("/./", validPos);
	while( markPos != std::string::npos && markPos < nameEndPos && markPos < anchorPos )
	{
		path.erase(markPos, 2);
		markPos = path.find("/./", validPos);
		nameEndPos = path.find('?');
		anchorPos = path.find('#');
		count ++;
	}

	markPos = path.find("//", validPos);
	nameEndPos = path.find('?');
	anchorPos = path.find('#');
	while( markPos != std::string::npos && markPos < nameEndPos && markPos < anchorPos )
	{
		path.erase(markPos, 1);
		markPos = path.find("//", validPos);
		nameEndPos = path.find('?');
		anchorPos = path.find('#');
		count ++;
	}

	markPos = path.find("/../", validPos);
	nameEndPos = path.find('?');
	anchorPos = path.find('#');
	while( markPos != std::string::npos && markPos < nameEndPos && markPos < anchorPos )
	{
		size_t endPos = markPos + 4;
		size_t beginPos = markPos > validPos ? (markPos - 1) : validPos;
		while( beginPos > validPos && path[beginPos] != '/' )
		{
			beginPos --;
		}

		path.erase(beginPos, endPos - beginPos - 1);
		markPos = path.find("/../", validPos);
		nameEndPos = path.find('?');
		anchorPos = path.find('#');
		count ++;
	}

	return count;
}
